package Linux五种IO模型;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @author coderFu
 * @create 2022/3/6
 * java NIO 标准使用
 * 底层使用 epoll 实现， 空轮询的bug 导致cpu飙升到 100%
 * Netty  封装好的NIO 操作。
 */
public class JavaNIOStandard {

    private static Selector selector;

    private static ByteBuffer byteBuffer;


    // 静态初始化一个 selector
    static {
        try {
            selector = Selector.open();
            byteBuffer = ByteBuffer.allocate(1024);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        //  起手四四句话
        // 创建服务端的serverSocketChannel 设置为非阻塞
        // 绑定端口 注册到建立链接的选择器中
        try{
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(9992));
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务端的端口为"+serverSocketChannel.getLocalAddress());
            // 开启一个线程处理 链接和数据读取操作
            new ReactorDemo().start();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    // 定义一个内部来处理 ，共享外部的资源
    private static class ReactorDemo extends  Thread {

        @Override
        public void run() {
            try{
                selector();
            }catch (IOException e){
                e.printStackTrace();
            }
        }

        public void selector() throws IOException{
             // 如果没有数据，处于阻塞状态
            while (selector.select()>0){
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    if (key.isAcceptable()){
                      accept(key);
                    }else if (key.isReadable()){
                        read(key);
                    }
                    // 必须要从事件中移除，否则下次事件发生时不会被感知到
                    iterator.remove();
                }
            }
        }

        private void read(SelectionKey key) throws IOException{

            // 处理客户端 channel 中的信息
            SocketChannel channel = (SocketChannel) key.channel();
            //申请缓冲区
            // 将数据读入缓冲区
            int count = channel.read(byteBuffer);
            if (count >0){
                // 如果读入了数据，将 buffer改为读模式
                byteBuffer.flip();
                // 确定编码格式，将数据解析出来
                String data = StandardCharsets.UTF_8.decode(byteBuffer).toString();
                // 一次读取一个字节 弊端  不能整行读取数据
                System.out.println(data);
                System.out.println(byteBuffer);
                byteBuffer.clear();
            }else if (count == -1){
                // 表示 连接已关闭
                channel.close();
            }
        }

        private void accept(SelectionKey key) throws IOException{
            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
            // 获取客户端的channel
            SocketChannel clientChanel = serverChannel.accept();
            // 做判空处理
            if (clientChanel != null){
                clientChanel.configureBlocking(false);

                InetSocketAddress localAddress = (InetSocketAddress) clientChanel.getLocalAddress();
                String hostName = localAddress.getHostName();
                System.out.println("接收到来自"+hostName+"的连接请求");
                clientChanel.register(selector,SelectionKey.OP_READ);
            }
        }
    }
}

